package net.apexcns.core.web.file;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import java.io.File;

import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;


import net.apexcns.core.web.util.ServletUtil;

import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.lang.ArrayUtils;
import org.apache.log4j.Logger;


/**
 * @author Sylvain Vieujot (latest modification by $Author: whseo $)
 * @version $Revision: 1.1 $ $Date: 2010/06/29 05:52:49 $
 */
@SuppressWarnings("unchecked")
public class MultipartRequestWrapper
        extends HttpServletRequestWrapper
{
	private Logger logger = Logger.getLogger(MultipartRequestWrapper.class);
    public static final String UPLOADED_FILES_ATTRIBUTE = "org.apache.myfaces.uploadedFiles";
    public static final String WWW_FORM_URLENCODED_TYPE = "application/x-www-form-urlencoded";

    HttpServletRequest request = null;
    @SuppressWarnings("rawtypes")
    HashMap parametersMap = null;
    ServletFileUpload fileUpload = null;
    @SuppressWarnings("rawtypes")
    HashMap fileItems = null;
    int maxSize = 100 * 1024 * 1024 ;
    int thresholdSize;
    String repositoryPath;
    HttpServletResponse response;

    public MultipartRequestWrapper(HttpServletRequest request, HttpServletResponse response) {
    	this(request, response, 100 * 1024 * 1024, -1, System.getProperty("java.io.tmpdir"));
    }

    public MultipartRequestWrapper(HttpServletRequest request, HttpServletResponse response,
                                   int maxSize, int thresholdSize,
                                   String repositoryPath){
        super( request );
        this.request = request;
        this.maxSize = maxSize;
        this.thresholdSize = thresholdSize;
        this.repositoryPath = repositoryPath;
        this.response = response;
    }
    
    @SuppressWarnings("rawtypes")
    private void parseRequest() {
		DiskFileItemFactory factory = new FileItemFactory();
		factory.setSizeThreshold(thresholdSize); 
		factory.setRepository(new File(repositoryPath)); 
		fileUpload = new ServletFileUpload(factory);
		fileUpload.setSizeMax(maxSize);

		String charset = request.getCharacterEncoding();
		fileUpload.setHeaderEncoding(charset);

		List requestParameters = null;
		try {
			requestParameters = fileUpload.parseRequest(request);
		} catch (FileUploadBase.SizeLimitExceededException e) {

			if (logger.isInfoEnabled())
				logger.info("user tried to upload a file that exceeded file-size limitations.", e);

			requestParameters = Collections.EMPTY_LIST;
			
			ServletUtil.printHtml(response, "<script>alert('업로드 사이즈 제한 100MB 넘을 수 없습니다.');history.back();</script>");

		} catch (FileUploadException fue) {
			logger.error("Exception while uploading file.", fue);
			requestParameters = Collections.EMPTY_LIST;
		}

		parametersMap = new HashMap(requestParameters.size());
		fileItems = new HashMap();

		for (Iterator iter = requestParameters.iterator(); iter.hasNext();) {
			FileItem fileItem = (FileItem) iter.next();

			if (fileItem.isFormField()) {
				String name = fileItem.getFieldName();

				// The following code avoids commons-fileupload charset problem.
				// After fixing commons-fileupload, this code should be
				//
				// String value = fileItem.getString();
				//
				String value = null;
				if (charset == null) {
					value = fileItem.getString();
				} else {
					try {
						//value = new String(fileItem.get(), charset);
						value = fileItem.getString(charset);
					} catch (UnsupportedEncodingException e) {
						value = fileItem.getString();
					}
				}

				addTextParameter(name, value);
			} else { // fileItem is a File
				if (fileItem.getName() != null) {
//					addFileItem(fileItem.getFieldName(), fileItem);
					FileItem[] item = (FileItem[]) fileItems.get(fileItem.getFieldName());
					fileItems.put(fileItem.getFieldName(), ArrayUtils.add(item, fileItem));
				}
			}
		}

		// Add the query string paramters
		for (Iterator it = request.getParameterMap().entrySet().iterator(); it.hasNext();) {
			Map.Entry entry = (Map.Entry) it.next();

			Object value = entry.getValue();

			if (value instanceof String[]) {
				String[] valuesArray = (String[]) entry.getValue();
				for (int i = 0; i < valuesArray.length; i++) {
					addTextParameter((String) entry.getKey(), valuesArray[i]);
				}
			} else if (value instanceof String) {
				String strValue = (String) entry.getValue();
				addTextParameter((String) entry.getKey(), strValue);
			} else if (value != null)
				throw new IllegalStateException("value of type : " + value.getClass() + " for key : " + entry.getKey()
						+ " cannot be handled.");

		}
    }

    private void addTextParameter(String name, String value){
        if( ! parametersMap.containsKey( name ) ){
            String[] valuesArray = {value};
            parametersMap.put(name, valuesArray);
        }else{
            String[] storedValues = (String[])parametersMap.get( name );
            int lengthSrc = storedValues.length;
            String[] valuesArray = new String[lengthSrc+1];
            System.arraycopy(storedValues, 0, valuesArray, 0, lengthSrc);
            valuesArray[lengthSrc] = value;
            parametersMap.put(name, valuesArray);
        }
    }

    @SuppressWarnings("unused")
    private void addFileItem(String name, CmmFileItem value){
        if( ! fileItems.containsKey( name ) ){
        	CmmFileItem[] valuesArray = {value};
            fileItems.put(name, valuesArray);
        }else{
        	CmmFileItem[] storedValues = (CmmFileItem[])fileItems.get( name );
            int lengthSrc = storedValues.length;
            CmmFileItem[] valuesArray = new CmmFileItem[lengthSrc+1];
            System.arraycopy(storedValues, 0, valuesArray, 0, lengthSrc);
            valuesArray[lengthSrc] = value;
            fileItems.put(name, valuesArray);
        }
    }

    @SuppressWarnings("rawtypes")
    public Enumeration getParameterNames() {
        if( parametersMap == null ) parseRequest();

        return Collections.enumeration( parametersMap.keySet() );
    }

    public String getParameter(String name) {
        if( parametersMap == null ) parseRequest();

        String[] values = (String[])parametersMap.get( name );
        if( values == null )
            return null;
        return values[0];
    }

    public String[] getParameterValues(String name) {
        if( parametersMap == null ) parseRequest();

        return (String[])parametersMap.get( name );
    }

    @SuppressWarnings("rawtypes")
    public Map getParameterMap() {
        if( parametersMap == null ) parseRequest();

        return parametersMap;
    }

    // Hook for the x:inputFileUpload tag.
    public FileItem getFileItem(String fieldName) {
        if( fileItems == null ) parseRequest();

        return (FileItem) fileItems.get( fieldName );
    }

    /**
     * Not used internally by MyFaces, but provides a way to handle the uploaded files
     * out of MyFaces.
     */
    @SuppressWarnings("rawtypes")
    public Map getFileItems(){
        if( fileItems == null ) parseRequest();
        return fileItems;
    }


    public Object getAttribute(String string) {
        if (string.equals(UPLOADED_FILES_ATTRIBUTE)) {
            return getFileItems();
        }
        return super.getAttribute(string);
    }
    
    public String getContentType()
    {
      return WWW_FORM_URLENCODED_TYPE;
    }     
}